﻿// -------------------------------------------------------------------------------------------
// <copyright file="ToolRerunHelper.cs" company="MapWindow OSS Team - www.mapwindow.org">
//  MapWindow OSS Team - 2015
// </copyright>
// -------------------------------------------------------------------------------------------

using System;
using System.Linq;
using MW5.Api.Interfaces;
using MW5.Api.Static;
using MW5.Plugins.Interfaces;
using MW5.Plugins.Services;
using MW5.Tools.Model;
using MW5.Tools.Model.Layers;
using MW5.Tools.Model.Parameters;
using MW5.Tools.Model.Parameters.Layers;

namespace MW5.Tools.Helpers
{
    /// <summary>
    /// Extension method for rerunning the tool.
    /// </summary>
    internal static class ToolRerunHelper
    {
        /// <summary>
        /// Returns filename of the output.
        /// </summary>
        public static string GetOutputLocation(this IParametrizedTool tool)
        {
            if (tool == null) return string.Empty;

            var parameter = tool.Parameters.OfType<OutputLayerParameter>().FirstOrDefault();
            if (parameter != null)
            {
                return parameter.GetValue().Filename;
            }

            return string.Empty;
        }

        /// <summary>
        /// Removes output layers generated by the execution of the tool. Layers are removed both from map and from the disk.
        /// </summary>
        public static bool RemoveOutputs(this IParametrizedTool tool, IAppContext context, ILayerService layerService)
        {
            foreach (var output in tool.GetOutputs())
            {
                var cache = output.DatasourcePointer;

                if (cache == null)
                {
                    // it's normal in case tool execution failed or was interrupted
                    continue;
                }

                if (cache.LayerHandle != -1)
                {
                    // it's only in-memory, so it's enough to remove it from map
                    if (!layerService.RemoveLayer(cache.LayerHandle))
                    {
                        MessageService.Current.Warn("Failed to remove output layer from the map.");
                        return false;
                    }
                }
                else if (cache.LayerIdentity != null)
                {
                    if (context.Layers.Any(l => l.Identity == cache.LayerIdentity))
                    {
                        if (!layerService.RemoveLayer(cache.LayerIdentity))
                        {
                            MessageService.Current.Warn("Failed to remove output layer from the map.");
                            return false;
                        }
                    }

                    if (!GeoSource.Remove(cache.LayerIdentity.Filename))
                    {
                        MessageService.Current.Warn("Failed to remove output layer from disk.");
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// Reopens input datasources from LayerParameterBase.ClosePointer.
        /// </summary>
        /// <exception cref="System.ApplicationException">Invalid call to ParameterCollection.ReopenDatasources.</exception>
        public static bool ReopenDatasources(this ParameterCollection parameters, IAppContext context)
        {
            foreach (var p in parameters.OfType<LayerParameterBase>())
            {
                var info = p.Value as IDatasourceInput;
                if (info == null)
                {
                    throw new ApplicationException("Invalid call to ParameterCollection.ReopenDatasources.");
                }

                var newInfo = p.ClosedPointer.ReopenDatasource(context, info);
                if (newInfo == null)
                {
                    MessageService.Current.Warn("Failed to reopen datasource for parameter: " + p.Name);
                    parameters.CloseInputDatasources();
                    return false;
                }

                p.SetToolValue(newInfo);
            }

            return true;
        }

        /// <summary>
        /// Reopens datasource which served as input for GisTool. The datasource will be searched
        /// for among open layers, including in-memory layers and if not present, reoped from the disk.
        /// </summary>
        private static IDatasourceInput ReopenDatasource(
            this DatasourcePointer ds,
            IAppContext context,
            IDatasourceInput oldInput)
        {
            int layerHandle = ds.LayerHandle;
            if (layerHandle != -1)
            {
                var layer = context.Layers.ItemByHandle(layerHandle);

                return ReopenLayerInput(layer, oldInput);
            }

            var identity = ds.LayerIdentity;
            if (identity != null)
            {
                // maybe it opened
                var layer = context.Layers.FirstOrDefault(l => l.Identity == identity);

                if (layer != null)
                {
                    return ReopenLayerInput(layer, oldInput);
                }

                // if not, let's try to open
                var source = GeoSource.OpenFromIdentity(identity) as ILayerSource;
                if (source != null)
                {
                    return new DatasourceInput(source);
                }
            }

            return null;
        }

        /// <summary>
        /// Creates instance of layer input for the specified layer.
        /// </summary>
        private static IDatasourceInput ReopenLayerInput(ILayer layer, IDatasourceInput oldInput)
        {
            if (layer == null)
            {
                return null;
            }

            var input = new LayerInput(layer);

            var vector = oldInput as IVectorInput;
            if (vector != null)
            {
                input.SelectedOnly = vector.SelectedOnly;
            }

            return input;
        }
    }
}